home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1995 October
/
EnigmA AMIGA RUN 01 (1995)(G.R. Edizioni)(IT)[!][issue 1995-10][Aminet 7].iso
/
Aminet
/
comm
/
tcp
/
AmiTCPtxt_20.lha
/
doc
/
maintainertext.txt
< prev
next >
Wrap
Text File
|
1993-08-13
|
98KB
|
2,788 lines
Chapter 4
Internal Description
4.1 Source File Structure
The source tree of AmiTCP/IP is dividedto few main parts:
1. Directory sys contains BSD Unix system header files, which are ported
to the Amiga environment.
2. Directory net contains modules for the general networking services.
They are independent of any particular protocol family.
3. Directory netinet contains the modules for the internet (inet)
protocol family. IP, ICMP, TCP and UDP are internet protocols.
4. Directory kern contains modules, which provide the BSD kernel
environment for the networking routines.
5. Directory api contains the BSD socket compatible shared library API
code.
The filesare briefly described in the following paragraphs. Note that
some of the header files are actually located to the NETINCLUDE:1
directory.
4.1.1 System Include Files
General Headers
sys/cdefs.h Definitions to harmonize various C language dialects.
sys/param.h General machine independent parameter definitions.
sys/time.h Definition of structure timeval.
sys/types.h Common C type definitions and file descriptorset macros
for select().
BSD Socket API
________________________________
1See section 3.1.3 on page 30.
69
70 Section 4.1 AmiTCP/IP System Manual
sys/errno.h Error code definitions for system functions.
sys/ioctl.h Definitions for socket IO control.
sys/socket.h Definitions related to sockets: types, address
families, options and prototypes.
sys/uio.h IO structure definition for sendmsg() and recvmsg()2.
BSD Kernel Data Structures
sys/domain.h Domain structure and global domain pointer declarations.
sys/kernel.h Global kernel variable definitions (used throughoutthe
kernel).
sys/malloc.h Defines the bsd_malloc() and bsd_free() functions.
sys/mbuf.h Mbuf structure definition and inline macros.
sys/protosw.h Protocol switch structure definition with related
definitions (options, actions, etc.).
sys/queue.h General queue data structures.
sys/socketvar.h Socket structure definition and related utility
macros.
sys/synch.h Declarations for tsleep and spl functions.
sys/syslog.h Logging related definitions.
sys/systm.h System wide definitions and prototypes.
4.1.2 Protocol Independent Network Routines
Network Interface
net/if.c General network interface routines.
net/if.h Defines the interface for network adapter drivers.
net/if_arp.h General protocol independent ARP structures.
net/if_dl.h Defines the Link Level sockaddr structure.
net/if_loop.c Loopback device routines.
net/if_sana.c Interface module for the SANA-IInetwork adapter
drivers.
net/if_sana.h Defines the interface for the SANA-II network adapter
drivers.
net/if_types.h Interface type numbers. Obsolete.
net/netisr.h and
net/netisr.c Defines the network input queue scheduling routines.
net/sana2arp.c ARP routines for Sana-II devices.
net/sana2arp.h SANA-II private ARP headers.
net/sana2copybuff.c Buffer management routines called from SANA-II
________device_driver.__________
2These functions are not implemented in this release of the AmiTCP/IP
System Manual AmiTCP/IP Section 4.1 71
net/sana2errno.h SANA-II error lists and printing header.
net/sana2request.c Moduleto handle SANA-II IO Requests.
net/sana2request.h Inlineroutines to manage different SANA-II IO
requests.
net/sana2tags.c Common customization parameters for the sana_softc
network interface.
net/sana2tags.h Defines the tags to customize sana_softc network
interface.
netlib:sana2perror.c Printing routines for SANA-II error messages.
Routing
net/radix.c Routines for searching, adding and removing data items in
the radix binary tree.
net/radix.h Defines the radix tree data structures.
net/route.c General, protocol independent routing functions.
net/route.h Defines data structures for routing entries,tables and
statistics.
net/rtsock.c The socket interface to the routing information.
Raw Sockets
net/raw_cb.c Routines to manage the rawprotocol control blocks.
net/raw_cb.h The raw protocol control block definition.
net/raw_usrreq.c The raw socket interface to the low level protocols
and network adapters.
4.1.3 Internet Protocol Modules
Inet Domain
netinet/in.c Generic Internet addressing routines.
netinet/in.h Protocol numbers, port conventions, inet address
definitions.
netinet/in_cksum.c Calculates checksum for internet protocol headers.
netinet/in_pcb.c Generic internet protocol routines, binding,
addressing.
netinet/in_pcb.h Declares generic internet protocol control block.
netinet/in_proto.c Defines internet protocol control blocks.
netinet/in_systm.h Some network byte order type definitions.
netinet/in_var.h Define an interface address structure for internet.
IP, ICMP
netinet/icmp_var.h ICMP statistics.
netinet/ip.h IP packet header, packet options, timestamp.
72 Section 4.1 AmiTCP/IP System Manual
netinet/ip_icmp.c Routines to generate, receive and reflect ICMP
packets.
netinet/ip_icmp.h ICMP packet structure.
netinet/ip_input.c IP input, packet reassemble and packet forwarding.
netinet/ip_output.c IP output, packet fragmenting.
netinet/ip_var.h Define IP statistics, external IP packet header,
reassemble queues structures.
netinet/raw_ip.c Provide a raw interface to the IP protocol.
TCP
netinet/tcp.h Define the TCP packet structure.
netinet/tcp_debug.c TCP debugging routines.
netinet/tcp_debug.h Header for TCP debugging.
netinet/tcp_fsm.h TCP Finite State Machine states.
netinet/tcp_input.c TCP input routines.
netinet/tcp_output.c TCP output routines.
netinet/tcp_seq.h TCP sequence numbering.
netinet/tcp_subr.c Various TCP subroutines for initializing,
connection maintenance etc.
netinet/tcp_timer.c TCP timeout routines.
netinet/tcp_timer.h TCP timing constants.
netinet/tcp_usrreq.c Process TCP user requests (send, timeout),
attach, disconnect.
netinet/tcp_var.h Define TCP control block and TCP statistics.
netinet/tcpip.h Define the overlaid TCPIP packet header structure.
UDP
netinet/udp.h Define the UDP packet structure.
netinet/udp_usrreq.c Routines to UDP output, input and notification.
netinet/udp_var.h Define UDPIP packet overlay, UDP statistics.
4.1.4 BSDKernel Service Modules
BSD Kernel Support
kern/amiga_includes.h Include file which includes all amiga specific
include files.
kern/amiga_main.c Main module of the AmiTCP/IP.
kern/amiga_subr.h Miscellaneous function definitions (usually short
inline functions).
kern/amiga_time.c Timer module for the timeout functions.
kern/amiga_time.h Amiga timer.device related functions.
System Manual AmiTCP/IP Section 4.1 73
kern/kern_malloc.c Malloc & free related functions.
kern/kern_synch.c tsleep and spl function definitions.
kern/uipc_mbuf.c Mbuf functions.
Socket Level Functions
kern/uipc_domain.c Domain functions, especially pfslowtimo() and
pffasttimo().
kern/uipc_socket.c Higher level so-level functions (socreate(),
sobind() etc.).
kern/uipc_socket2.c Lower level so-level functions (soisconnecting(),
soqremque(), sowakeup() etc.).
NETTRACE Maintenance Process Support
kern/amiga_config.c Configuration and ARexx command support routines.
kern/amiga_config.h Defines the structure for configuration
variables.
kern/amiga_cstat.c Query support routines.
kern/amiga_log.c Functions for initialization of log task and code
for task itself.
kern/amiga_log.h Header file for logging functions.
kern/amiga_netdb.c Network database parsing and support functions.
kern/amiga_netdb.h Declares the network database structures and
types.
kern/amiga_rexx.c Arexx-interface.
kern/amiga_rexx.h Arexx-interface definitions.
kern/config_var.awk Awk script to generate both code and
documentation from the file kern/variables.src.
kern/subr_prf.c Interfaces for (s)printf(), panic() and log()
kern/variables.src Definition and documentation for configuration
variables
4.1.5 BSDSocket API
Shared Library Interface
api/allocdatabuffer.c Client data buffer allocation functions.
api/allocdatabuffer.h Client data buffer allocation definitions.
api/amiga_api.c Contains API initialization and deinitialization
functions and data. Opening and Closing of the AmiTCP/IP socket
library bases.
api/amiga_api.h Description of the SocketBase structure (forinternal
AmiTCP/IP use only), and some inline functions relatedto the API
functionality.
74 Section 4.1 AmiTCP/IP System Manual
api/amiga_libcallentry.h Included in every module which defines
AmiTCP/IP socket library calls.
api/amiga_libtables.c Exec library base list and AmiTCP/IP socket
library function tables for MakeLibrary().
api/amiga_raf.h Compiler dependent macros for Register Argument
Functions.
api/apicalls.h Includes either api/apicalls_gnuc.h or
api/apicalls_sasc.h.
api/apicalls_gnuc.h Inline functions for internal API calls for GNUC.
api/apicalls_sasc.h Inline functions for internal API calls for SASC.
API Functions
api/amiga_generic.c General Unix system calls related to file
descriptors ported for AmiTCP/IP socket library. There are also
some Amiga specific extensions to the BSD Unix system calls in
this module.
api/amiga_libcalls.c Inet library functions (link library in Unix
system). These inet functions are provided as a part of the
AmiTCP/IP socket library.
api/amiga_syscalls.c Standard BSD style socket functions ported to
the AmiTCP/IP socket library.
api/gethostnamadr.c Functions that calls resolver in order to obtain
host information from nameserver. Calls netdatabase functions if
information can not be resolved.
api/gethtbynamadr.h Prototypes for two functions in api/getxbyy.h.
api/getxbyy.c Host (without nameserver), network, service ja protocol
query functions (also a link library in unix system).
api/hostbuf.h Structure definition for host queries and few minor
netdb stuff.
Resolver Functions
api/arpa_nameser.h Information for nameserver query.
api/res_comp.c Routines to translate domain names between
conventional ascii and the compressed format used in queries.
api/res_debug.c Functions that output debugging information3
api/res_init.h Resolver initializer function. Very simple in
AmiTCP/IP implementation.
api/res_mkquery.c Function that forms a domain name query in buffer.
api/res_query.c Functions to generate query sequence and append
domains to incomplete hostnames.
api/res_send.c Function to send and receive query to and from a
nameserver, respectively.
api/resolv.h Resolver datatypes, defines, variables and prototypes.
________________________________
3If RES_DEBUG defined at compile time
System Manual AmiTCP/IP Section 4.2 75
4.1.6 Miscellaneous Files
Makefiles
GCCOPTS Compiler options forthe GCC.
GNUmakefile Makefile used with GNU make in HP-UX workstations, where
the source tree is maintained.
SCOPTIONS Compiler options for the SAS/C 6.x
Smakefile Makefile for SAS/C smake 6.x to compile the AmiTCP/IP.
Revision Support
bsdsocket.library_rev.rev Contains revision numberof the current
version (number of the latest build). This file is updated with
the BumpRev utility4.
bsdsocket.library_rev.h Automaticallygenerated by BumpRev. Contains
build date, version and revision strings.
SAS/C 6.x GST Support
all_includes.c C file for GST generation. Just includes the
all_includes.h file.
all_includes.h Includes all header files needed by theAmiTCP/IP,
which are suitable for inclusion in a GST. Note that any headers
with inline functions can not be included in a GST.
Other Files
conf/conf.h Static configuration information in form of preprocessor
defines. Every C module must include this as the first include
file.
conf/rcs.h RCS header inclusion macro for GCC. InSAS/C this macro is
defined in the SCOPTIONS file.
protos/*/*.h Prototypes for functions in various modules.
4.2 AmiTCP/IP Initialization
All more or less distinct modules of theAmiTCP/IP must be initialized
before they can be used for the real work. Initialization is done in
module kern/amiga_main.c, which definesfunctions init_all() and
deinit_all(). The main() function calls the init_all()after it has done
all needed local initializations. If init_all() failsthe deinit_all() is
called to clean up.
________________________________
4The BumpRev is supplied by Commodore.
76 Section 4.2 AmiTCP/IP System Manual
4.2.1 init _all()
This function calls the initialization routines of all modules which have
such. This must be done carefully in the correct partial order.
There arefew general heuristics which can be applied on initialization
(with respect to the ordering requirements):
1. Initialize the modules for which the initialization cannot fail.
This includes semaphore initializations and such.
2. Initialize the modules which are most likely to fail, e.g. large
memory allocations, modules which require recent versions of some
libraries etc.
3. Initialize modules left over by rules 1 and 2.
Semaphoreinitializations are first, because later steps may use them
to protect against race conditions.
The initialization process of the AmiTCP/IP is fully reversible. If
the initialization fails in any step thedeinit _all() function can be
used to collect the garbage.
init_all() does the initializations in following order, returning
nonzero if all steps succeed. Numbers in the parenthesis are the section
and page numbers for more information, respectively:
1. malloc_init() initializes the malloc_semaphore. This is first since
later steps may want to use bsd_malloc() to allocatememory (5.2.2,
95).
2. spl_init() initializes the priority level subsystem,which is used
throughout the code to protect critical sections (5.3.1, 96).
3. sleep_init() initializes the sleep_semaphore and the sleep queues
(5.3.2, 97).
4. readconfig() reads in and parses the command line arguments and the
configuration file. This is the first phase which is expected to
fail. This is done at the beginning in order to allow other
initialization functions to use the configuration information given.
Note that since the logging subsystem is not initialized yet,
readconfig() cannot log any errors encountered (4.11, 91).
5. log_init() initializes the logging subsystem. From now on other
initialization functions can log needed error messages (4.8, 89).
6. mbinit() allocates memory to be used by the protocols. This is a
candidate to fail if the memory is nearly exhausted (4.5, 79).
7. timer_init() initializes the timeout module. This requires version
36 or greater of the operating system. Returns the signal mask to
wait for the timeout messages (5.1, 93).
8. api_init() initializes and creates the master socketbase structure
(4.10.2, 91).
9. res_init() initializes resolver structure and semaphore.
System Manual AmiTCP/IP Section 4.3 77
10. sana_init() initializes the SANA-II network interface module. This
returns the signal mask to be used for waiting the network related
messages (4.7, 84).
11. domaininit() initializes all configured protocols. This is left at
the end of the initialization, since this requires the other parts of
the system to be initialized.
12. readnetdb() initializes and reads in the network data base
information from ENV:AmInet/netdb (4.12, 92).
13. api_show() makes the shared library interface visible on the Exec
library list (4.10.2, 91).
4.2.2 deinit _all()
deinit_all() is the reverse of init_all(); it deinitializes required
modules in reverse order with respect tothe initialization.
If the initialization process for a module does not allocate any
resources, then there is no deinitialization function for that module.
The deinitialization functions called are (in this order):
1. api_hide() removes the library from the Exec librarylist, so no-one
can open the library any more (4.10.2, 91).
2. sana_deinit() deinitializes the network driver module (4.7, 84).
3. api_deinit() deinitializes the API (4.10.2, 91).
4. timer_deinit() deinitializes the timer module (5.1,93).
5. mbdeinit() frees all memory used by mbufs (4.5, 79).
6. log_deinit() deinitializes the logging subsystem (4.8, 89).
4.3 The Main Module
The main() function is defined in the file kern/amiga_main.c. On startup
it initializes all modules by calling init _all(). After a successful
initialization it first starts the AmiTCP/IP timeouts by calling
timer_send(). Then it enters the event loop.
AmigaOS function Wait() returns when one of the signals specified in
the signal mask given as argument is received. The mask currently
specifies signals for SANA-II drivers, timeouts and the break signal.
When some set of these signals is received the Wait() returns and its
return value, which indicates the received signals, is checked. The
functions sana_poll() and timer_poll() are each called in turn if their
signal was received. They each return boolean tellingif they would like
to be called again before waiting again. The loop in which these
functions are called is terminated either when no one of the poll
functions wants to continue or when thebreak signal is received.
The pollfunctions do handle only one message at a time, so that any of
them should not starve.
78 Section 4.4 AmiTCP/IP System Manual
When thebreak signal is received the AmiTCP/IP terminates if no
library bases opened by API users are open any more. On termination all
reserved resources are freed by callingdeinit _all().
4.4 Protocol Entities
One design goal was to keep the protocolentities intact. This is
achieved through implementing all external dependencies of the protocol
entities. Fortunately the protocol entities in BSD arehighly
independent of other UNIX kernel services. For example, all dynamic
memory management is done through the memory buffer5 abstraction, which
means that we only had to provide the mbuf interface and the problem of
memory management was solved.
All protocol entities in a protocol family are defined in terms of a
protocol switch structure. This structure is fully defined in the header
file sys/protosw.h and [Leffler et al 1991b ]. The protocol switch
structure is defined as follows:
struct protosw -
short pr_type; /* socket type used for */
struct domain *pr_domain; /* domain protocol a member of */
short pr_protocol; /* protocol number */
short pr_flags; /* protocol flags */
/* protocol-protocol hooks */
void (*pr_input)(); /* input to protocol (from below) */
int (*pr_output)(); /* output to protocol (from above)*/
void (*pr_ctlinput)(); /* control input (from below) */
int (*pr_ctloutput)(); /* control output (from above) */
/* user-protocol hook */
int (*pr_usrreq)(); /* user request hook */
/* utility hooks */
void (*pr_init)(); /* initialization hook */
void (*pr_fasttimo)(); /* fast timeout (200ms) */
void (*pr_slowtimo)(); /* slow timeout (500ms) */
void (*pr_drain)(); /* flush any excess space possible */
";
Note thatthe actual prototypes for the function pointers are omitted,
see the file sys/protosw.h for full definition.
When protocol is started the pr_init() is called first to allow the
protocol to initialize all needed internal structures. Then the input
process will call pr_fasttimo() and pr_slowtimo() entries periodically if
defined6. The pr_drain() entry asks the protocol tofree all
non-critical memory buffers in a low-memory situation.
Protocolscall each other through the protocol--protocol interface. To
pass a packet up in the hierarchy a protocol calls the pr_input()-entry
of the protocol above it. pr_output()-entry is called when a protocol
wishes to pass a packet down in the protocol hierarchy (towards network).
________________________________
5or mbuf for short
6Member function is defined when the value of its address is not NULL.
System Manual AmiTCP/IP Section 4.5 79
Protocolssend control information to each other through the
pr_ctlinput() and pr_ctloutput() entries.
All requests coming from the API are dispatched through the pr_usrreq()
entry.
4.5 Memory Management
As stated earlier, the memory managementof the protocol stack is done
with memory buffers. An mbuf is a structure containinglittle amount of
storage (usually 128 bytes). Some bytes of this storage are used for the
header, but most of it is used to storeuser data. These small buffers
are linked together to get storage for larger data.
Mbufs arehighly efficient in a network protocol environment where it
must be able to attach and strip protocol headers with minimum overhead
and most importantly, without copying the data as doing so. When data is
stored in an mbuf chain, attaching a header is achieved by simply linking
the mbuf containing the header to the head of the chain. Removing a
header is also done simply by removing the first mbuf or by incrementing
the data pointer inside the mbuf.
In general there are two types of mbufs. Ones with an packet header
and ones without. An Mbuf with packet header is used as the first mbuf
of every packet. This header contains extra information needed per
packet. See the header file sys/mbuf.h for the mbuf header definition.
Mbufs canbe chained in two dimensions. First they may be linked to
form the storage for the whole message. Second these messages may be
linked together so that the boundaries of messages are maintained. This
second feature is mainly used by messageoriented protocols such as UDP.
To gain efficiency an mbuf may have a reference to external memory page
(a cluster), where a big message is copied instead of splitting it apart
to many mbufs. The main advantage of this feature is avoidance of
copying the data when sending it with TCP, since the clusters are shared
between copies7.
4.5.1 Mbuf Functions
Mbufs are accessed through set of functions which can be grouped as
follows:
Maintenance
int mb_check_conf(void *dp, LONG newvalue)
Check configurable variable whose address is dp. Return TRUE if the
newvalue is acceptable value for that variable. See section 4.11 for
information about the configuration.
BOOL mbinit(void)
________________________________
7TCP must keep a copy of the sent data for possible retransmissions.
80 Section 4.5 AmiTCP/IP System Manual
Initialize the whole mbuf-subsystem. Allocate a chunk of mbufs and
clusters (using m_alloc() and m_clalloc). Must be called before any
other mbuf-related function (except that mb_check_conf() can be
called anytime).
void mbdeinit(void)
Free all resources used by mbuf subsystem. Must be called as the
last mbuf-related function in the program.
BOOL m_alloc(int howmany, int canwait)
Allocate howmany mbufs and place them on the mbuf free list. If
canwait is true the caller can wait if memory is not readily
available.
BOOL m_clalloc(int ncl, int canwait);
Allocate ncl mbuf clusters and place them on the cluster free list.
struct mbuf *m_retry(int i, int t)
Ask protocols to free space when short of memory and re-attempt to
allocate an mbuf.
void m_reclaim(void)
Ask protocols to free space when short of memory.
Allocation and Deallocation
struct mbuf *m_get(int canwait, int type)
Allocate an mbuf of type type. If no mbufs are available we can wait
for them if canwait is M_WAIT. Initialize mbuf to contain internal
data.
void MGET(struct mbuf *m, int canwait, int type)
This is an macro form of above.
struct mbuf *m_gethdr(int canwait, int type)
Allocate an mbuf of type type. If no mbufs are available we can wait
for them if canwait is M_WAIT. Initialize mbuf to contain a packet
header and internal data.
void MGETHDR(struct mbuf *m, int canwait, int type)
This is a macro form of above.
struct mbuf *m_getclr(int canwait, int type)
Allocate an mbuf of type type. If no mbufs are available we can wait
for them if canwait is M_WAIT. If allocation succeeds the data buffer
is zeroed before returning.
MCLALLOC(struct mcluster *p, int canwait)
A macro to allocate an mbuf cluster. The result is placed in p.
System Manual AmiTCP/IP Section 4.5 81
MCLGET(struct mcluster *p, int canwait)
A macro to add a cluster to a normal mbuf. M_EXT flag of the mbufis
set on success.
struct mbuf *m_free(struct mbuf *m)
Free an mbuf. Next mbuf in the chain is returned, if any.
void MFREE(struct mbuf *m, struct mbuf *n)
This is an macro form of above. Successor mbuf is returned in n.
MCLFREE(struct mcluster *p)
A macro to free an mbuf cluster.
void m_freem(struct mbuf *m)
Free the whole chain of mbufs starting from m.
Utility Functions
struct mbuf *m_copym(struct mbuf *m, intoff0, int len, int canwait)
Make a copy of an mbuf chain starting off0 bytes from the beginning
of m, continuing for len bytes. If len is M_COPYALL, copy to endof
mbuf.
void m_copydata(struct mbuf *m, int off,int len, caddr_t cp)
Copy data from an mbuf chain starting off bytes from the beginning,
continuing for len bytes, into buffer cp.
struct mbuf *m_prepend(struct mbuf *m, int len, int canwait)
Prepend a chain of mbufs (m) with new mbuf with len bytes allocated
from the first mbuf aligned on a long word boundary.
void M_PREPEND(struct mbuf *m, int plen,int canwait)
Macro version of above optimized for the most general cases.
void M_COPY_PKTHDR(struct mbuf *to, struct mbuf *from)
Macro for copying an mbuf packet header from from to to. from must
have flag M_PKTHDR set, and to must be empty.
void M_ALIGN(struct mbuf *m, int len)
Macro to set the m data pointer of a newly--allocated mbuf (with
m_get()/MGET()) to place an object of the size len at the end of the
mbuf, long word aligned.
void MH_ALIGN(struct mbuf *m, int len)
As above, but for mbufs allocated with m_gethdr()/MGETHDR() or
initialized by M_COPY_PKTHDR().
int M_LEADINGSPACE(struct mbuf *m)
Compute the amount of space available before the current start of
data in an mbuf.
82 Section 4.6 AmiTCP/IP System Manual
int M_TRAILINGSPACE(struct mbuf *m)
Compute the amount of space available after the end of data in an
mbuf.
void MCHTYPE(struct mbuf *m, type t)
Change mbuf to a new type.
void m_cat(struct mbuf *m, struct mbuf *n)
Concatenate mbuf chain n to m. Both chains must be of the same type.
m_adj(struct mbuf *mp, int req_len)
Trim req_len bytes from the head of the mbuf chain mp if req_len is
positive, else trim -- req_len bytes from the tail of the mbuf chain.
struct mbuf *m_pullup(struct mbuf *n, int len)
Rearrange an mbuf chain so that len bytes from the beginning of the
mbuf chain n are contiguous and in the data area of the mbuf so that
the data can be used as a structure.
Utility Macros
type t mtod(struct mbuf *m, type t)
Convert mbuf pointer to a pointer to the start of the data area of
the mbuf casted to type t.
struct mbuf *dtom(type *x)
Convert data pointer within an mbuf to mbuf pointer.
4.6 Concurrency Control
The protocol implementation in the BSD net/2 is driven by network and
timer interrupts and user processes calling the system functions. As the
whole protocol stack is moved inside normal AmigaOS process, some
modifications are in place.
The processor priority levels are the main concurrency control tool of
the BSD kernel. The levels defined are SPL0 (user level), SPLSOFTCLOCK,
SPLNET and SPLIMP (the most privileged level). Execution at a higher
level disables the execution at all lower levels. InAmiTCP/IP the
concurrency control is implemented either with semaphores (when
debugging) or with prevention of the task switches (Forbid()/Permit()),
see section 5.3.1 on page 96 for the implementation notes.
The protocol input and timeouts are driven by a single process that
manages the whole protocol stack. The process sends appropriate IO
requests to the timer device and the SANA-II device drivers in question.
Actions are then taken as response to the returned requests. Before any
protocol routines are called the priority level is raised either to
SPLSOFTCLOCK or SPLNET. After the function returns the priority level is
lowered back to SPL0 and the request issent back to the device driver.
System Manual AmiTCP/IP Section 4.7 83
On the API side the concurrent execution of system calls is mostly
prohibited, because in UNIX the system calls are atomic in the sense that
there is never more than one system callin execution. In AmigaOS the
shared library functions must be re-entrant so the protection must be
provided by the library functions themselves.
The priority at which the main process runs must be above the default
value of 0 to provide enough time to process the networking protocols.
On the other side there is no sense to drive the main process at greater
priority than the SANA-II device drivers.
4.7 Network Device Drivers
AmiTCP/IP uses standard SANA-II driversas its external network device
drivers. A little glue is needed to attach a SANA-II driver into BSD
net/2 code.
Network Interface
The BSD net/2 networking code provides aclean interface to the network
device drivers. The network interface provides a consistent interface
for all protocols that may be present inthe BSD Unix kernel. Each
hardware device is associated with an unique network interface which may
be used by one or more protocol families.
The network interface is flexible enough to attach different SANA-II
network device drivers into the AmiTCP/IP networking system. Common
part of all network interfaces is described in [Leffler et al 1991b ]:
struct ifnet -
char *if_name; /* name, e.g. "en" or "lo" */
short if_unit; /* sub-unit for lower level driver */
short if_mtu; /* maximum transmission unit */
short if_flags; /* up/down, broadcast, etc. */
short if_timer; /* time 'til if_watchdog called */
int if_metric; /* routing metric (external only) */
struct ifaddr *if_addrlist; /* linked list of addresses per if */
struct ifqueue -
struct mbuf *ifq_head;
struct mbuf *ifq_tail;
int ifq_len;
int ifq_maxlen;
int ifq_drops;
" if_snd; /* output queue */
/* procedure handles */
int (*if_init)(); /*init routine */
int (*if_output)(); /* output routine (enqueue) */
int (*if_start)(); /* initiate output routine */
int (*if_done)(); /*output complete routine */
int (*if_ioctl)(); /* ioctl routine */
int (*if_reset)(); /* bus reset routine */
int (*if_watchdog)(); /* timer routine */
/* generic interface statistics */
int if_ipackets; /* packetsreceived on interface */
84 Section 4.7 AmiTCP/IP System Manual
int if_ierrors; /* input errors on interface */
int if_opackets; /* packetssent on interface */
int if_oerrors; /* output errorson interface */
int if_collisions; /* collisions on csma interfaces */
/* end statistics */
struct ifnet *if_next;
";
Network interface for SANA-II devices are handled in the module
net/if_sana.c. This is the only module aware of SANA-II devices inside
the AmiTCP/IP network process. It hides most SANA-II specific details
from the rest of the code.
Module Initialization
ULONG sana_init(void)
This initialization routine is called at startup time before any
interfaces have been added to system. It creates the common message
port used for all SANA-II network interfaces. It also attaches the
loopback device into system. It returns the signal mask of the
message port, if its creation was successful.
void sana_deinit(void)
This routine frees all resources allocated by the SANA-II interface
module. It aborts all pending IO requests, frees them, closes
network device drivers and frees the corresponding network
interfaces. Finally it deletes the message port.
4.7.1 SANA-II Soft Network Interface
A message passing system based on the normal Exec IO requests is used to
transfer packets between the AmiTCP/IP and SANA-II devices. The IO can
be either synchronous or asynchronous. The SANA-II interface module has
a message port, which receives all fulfilled or aborted asynchronous IO
messages. A dispatch method (currently a dispatch function pointer) have
been added to all asynchronously sent IOrequests. Dispatching function
handles the received message in an appropriate way.
A messagemay contain received packet, some buffers allocated for the
sent message or an event mask. Dispatcher functions feed the received
data to the protocol input queues. If needed, the protocol input
routines are run. Dispatchers also free the memory allocated for the
sent packets, or relays events to the higher level protocols.
Because the interface for the SANA-II device driver must handle many
different protocols and network adapters, it has some private data hidden
from the rest of the system. The struct sana_softc network interface is
defined in file net/if_sana.h:
/*
* SANA-II Interface descriptor
* NOTE: most of the code outside will believe this to be simply
* a "struct ifnet". The other information is, on the other hand,
* our own business.
System Manual AmiTCP/IP Section 4.7 85
*/
struct sana_softc -
struct ifnet ss_if; /* network-visible interface */
struct in_addr ss_ipaddr; /* copy of ip address */
ULONG ss_hwtype; /* wiretype */
UBYTE ss_hwaddr[MAXADDRSANA]; /* Generalhardware address */
struct Device *ss_dev; /* pointer todevice */
struct Unit *ss_unit; /* pointer to unit */
VOID *ss_bufmgnt; /* magic cookie for buffer mngement */
UWORD ss_reqno; /* # of requests to allocate */
struct IOIPReq *ss_reqs; /* allocated requests*/
struct MinList ss_freereq; /* free requests */
#if INET
struct -
UWORD reqno; /* for listening ip packets */
UWORD sent;
ULONG type;
" ss_ip;
struct - /* for ARP */
UWORD reqno;
UWORD sent;
ULONG type; /* ARP packet type */
ULONG hrd; /*ARP header type */
struct arptable *table; /* ARP/IP table */
" ss_arp;
#endif /* INET */
UWORD ss_rawreqno; /* for raw packets */
UWORD ss_rawsent;
struct sana_softc *ss_next;
char ss_name[IFNAMSIZ]; /* namelives here */
";
There isan external interface to this structure via SIOCSSANATAGS and
SIOCGSANATAGS ioctls.
struct ifnet *iface_find(char *name)
This function initializes a network interface for given interface.
It is called on a non-existent interface from ifunit(). It tries to
open the appropriate SANA-II device driver, and if successful it
initializes a descriptor and calls if_attach() with it.
Its argument is the device driver name concatenated with a slash and
unit number. This name is used by AmiTCP/IP to open appropriate
unit of the SANA-II driver.
The initialization routine provides the specified SANA-II network
device unit with the CopyToBuf() and CopyFromBuf() function tags8.
Those tags are used to copy data to and from the internal buffers of
the network device. The taglist given to the device driver is
defined in file net/sana2copybuff.c.
After successful open iface_find() initializes the appropriate
members of the new sana_softc structure. It stores the device and
unit pointers, magic cookie for buffer management, hardware type, MTU
________________________________
8For discussion for these functions, see [SANA-II 1992 add ]
86 Section 4.7 AmiTCP/IP System Manual
and address length. It also searches for the hardware type specific
taglist, and sets the rest of interface parameters according the
taglist.
Next the interface initialization routine attaches the new network
interface into ifnet list with if_attach(). Then it initializes
interface with if_init().
Interface Routines
BOOL sana_poll(void)
The AmiTCP/IP processes received messages by calling this function.
It effectively hides the actual implementation from the rest of the
system. This routine is called when AmiTCP/IP receives the signal
allocated by sana_init(), and it returns TRUE, if itshould be called
again before Wait().
sana_poll() retrieves messages from the message port, dispatches them
and then runs the input queues.
int sana_output(struct ifnet *ifp, struct mbuf *m0,
struct sockaddr *dst, struct rtentry *rt)
This function is used as the if_output() method. It tries to get a
free IO request from the ss_freereq list. If no free request is
available it drops the packet. It attaches the packet m0 to request,
sets dispatching function to free_written_packet() and sends the
request to the device driver.
The raw packets to the SANA-II interface use the following variation
of socket address. The addressing family of the raw packets must be
AF_UNSPEC. Currently only the ARP uses the raw SANA-II packets.
/*
* A socket address for a generic SANA-II host
*/
struct sockaddr_sana2 -
u_char ss2_len;
u_char ss2_family;
u_long ss2_type;
u_char ss2_host[MAXADDRSANA];
";
void sana_ioctl(struct ifnet *ifp, int cmd, caddr_t data)
This function is used as the if_ioctl() method. SANA-II devices and
their respective network interfaces are configured via raw sockets by
the IoctlSocket() requests. When the sana_ioctl() gets the
SIOCSIFADDR request, it changes the IP address of the interface. The
SIOCGIFFLAGS ioctl is used to set the parameter flags of the
interface9.
Special SANA-II configuration is done with SIOCSSANATAGS ioctl. It
passes a tag list to the parse_sana_param_tags() function.
________________________________
9See file sys/ioctl.h for full listof all ioctls
System Manual AmiTCP/IP Section 4.7 87
void if_down(struct ifnet *ifp)
This function pulls into if_ioctl() from net/if.h. if_down() marks
the interface down and informs all affected network protocols about
the matter. If the interface handles SANA-II device, it calls
sana_down(), which handles the dirty work to put theinterface down.
static void sana_run(struct sana_softc *ssc, int n, struct ifaddr *ifa)
sana_run() configures the SANA-II interface and allocates the IO
requests to use with the SANA-II device driver. Because the SANA-II
device can be configured only once (see S2_CONFIGINTERFACE in
[SANA-II 1992 add ]) the initialization routine does not configure it.
Among other things the hardware address of the network adapter is set
in configuration process. This function is called by the SIOGSIFADDR
ioctl, which also sets the protocol address of the interface.
static void sana_up(struct sana_softc *ssc)
sana_up() marks interface up and enables the interface to listen the
packets from the network. It sends read request with an appropriate
packet type number and dispatch function to the device.
static BOOL sana_down(struct sana_softc*ssc)
sana_down() aborts all pending requests sent to a SANA-II device
driver.
static void sana_ip_read(struct sana_softc *ssc,
struct IOIPReq *req) and
static void sana_arp_read(struct sana_softc *ssc,
struct IOIPReq *req)
These dispatch functions are used to feed data from a network device
to the protocol input queues. The network interface has not the
input routines as members. Dispatching functions allocate mbufs for
the next packet and send IO requests again to the network device.
Statistics
BSD Network interface contains a lot ofexcessive statistical data. Most
of it is made redundant by the statistics gathered by the SANA-II driver.
Because network statistics are not retrieved by looking at /dev/kmem,
there is no need to gather BSD compatible statistics. A public ARexx
port, named AMITCP, is set up for statistics retrieval and we can use
appropriate SANA-II commands to get needed data when asked.
4.7.2 ARP
The requirements for the ARP implementation for a SANA-II interface
differ radically from the original implementation BSD. The original code
was written exclusively for the Ethernet, which has a global addressing
scheme and a fixed address length.
The SANA-II ARP (in the module net/sana2arp.c) holds a separate address
mapping cache for each interface. The number of entries in the cache may
88 Section 4.8 AmiTCP/IP System Manual
be configured at the run time. The hardware address length varies from
one interface to another. The mapping caches are hashtables with linked
lists, so there is no limitations in thebucket size. ARP Table locking
is done with a signal semaphore insteadof spln() functions.
The ARP table is externally accessed only by IoctlSocket() calls. With
the new SIOCGARPT ioctl the whole ARP cache may be read at once. There
is no need to awkwardly read /dev/kmem.
4.8 Logging
As everything is not predictable, programs like to inform the user about
certain situations to help the user and/or maintainer to get programs
work better. This is the motivation for log() and panic() functions.
The fact that the file I/O routines can't be called from interrupts must
be taken into account, since a program may want to inform the user even
while executing at an interrupt level.
Among thevery few functions of the AmigaOS which are callable from
interrupts are GetMsg() and PutMsg(). These are used to implement the
logging subsystem.
When AmiTCP/IP has something to tell to the user, it first checks if
there is any free messages available. There is only alimited number of
these messages to use, since they are preallocated during
initialization10. If there is no message available, a counter indicating
that the message could not be deliveredis incremented. If a free
message is available, the text given bycaller is printed into the buffer
of the message. Then the message is sent to the private port of the
NETTRACE task.
The NETTRACE task is created early in the initialization of the
AmiTCP/IP. This task waits for incomingmessages and when one arrives,
it prints the message to the log windowand/or log file. Then message is
freed by sending it back to AmiTCP/IP for reuse. If loss of log
messages is detected, log() is called totell that to the user.
The functions are defined as follows:
int log(unsigned long level, const char*fmt, ...)
This logs a message with format specified in exec.library/RawDoFmt(),
similar to the printf(). It is preceded with ``<N>'' where N is a
level as defined in file sys/syslog.h.
void panic(const char *fmt, ...)
When this function is called, we are in BIG trouble. If task, which
called this, is not AmiTCP/IP, we just halt this task and send a
message to AmiTCP/IP to halt immediately and free all resources.
Format is as in exec.library/RawDoFmt().
If AmiTCP/IP runs into a panic() it first patches all API functions
to return an error code to the caller. Then, if the panic() was not
in the context of the AmiTCP/IP it signals the AmiTCP/IP and halts.
As the AmiTCP/IP receives the signal, it send a message to the log
________________________________
10This is because memory cannotbe allocated from interrupt code.
System Manual AmiTCP/IP Section 4.9 89
and signals all application programs waiting for network to take
attention. As this is done, it opens an User Requester to inform the
user. After the user responds, AmiTCP/IP waits for all library
openers to close libraries and finally unloads itself from the
memory.
int printf(const char *fmt, ...)
Like the normal C--library printf (with format of
exec.library/RawDoFmt()) except that the printing is done using the
logging mechanism.
int sprintf(char *buf, const char *fmt,...)
As in a normal C--library. Format is as in exec.library/RawDoFmt().
All functions (except panic()) return number of printed (or logged)
characters.
Initialization Routines
BOOL log_init(void)
This function initializes NETTRACE subsystem by opening
intuition.library for opening UserRequest in the case of panic().
The the log messages are initialized to use by preallocating memory
for them.
Then NETTRACE is started and AmiTCP/IP waits for a signal from it.
If NETTRACE success in it's initialization, then it sends a message
back, which is then replied. If initialization fails, a variable is
set to specific value and CTRL-F is sent to the AmiTCP/IP. If all
this succeeds, the log messages are initialized and sent to
logReplyPort which works as a queue for the free messages.
void log_deinit(void)
This works as reverse to initialization process. If NETTRACE is
still running, a message is sent to it telling it to terminate. Then
AmiTCP/IP waits until the message is replied. Then the memory
reserved by the messages can be freed. Finally the intuition.library
is closed.
4.9 ARexx Interface
The ARexx port of the AmiTCP/IP is maintained by the NETTRACE task. The
messages are parsed with parseline() (defined in kern/amiga_config.c).
Initialization Routines
ULONG rexx_init(void)
This initialization routine is called at the startup time of the
NETTRACE process. It opens the utility.library and the
rexxsyslib.library to be used by the ARexx code and creates a public
90 Section 4.10 AmiTCP/IP System Manual
ARexx message port. The signal mask of the ARexx port is returned
upon a successful initialization.
void rexx_deinit(void)
Free all resources allocated by the ARexx interface module. First
the ARexx port is removed from the system's list of message ports so
that no-one is able find the port any more to send new messages.
Then all pending messages are returned with error code set. Finally
the ARexx port is deleted and libraries opened by rexx_init() are
closed.
Reply Routine
BOOL rexx_poll(void)
Checks if any ARexx messages has arrived and handles them one at a
time. The parseline() function is used to parse and execute the
given command. Returns TRUE if there might still be messages to
handle, otherwise the return value is FALSE.
4.10 Application Interface Concepts
4.10.1 SocketBase -- an Extension of the Task Structure
In Unix systems, where the network codeis integrated into the kernel, a
process structure holds fields for per-process information of network
related data. In AmiTCP/IP, where socket API is implemented as a shared
library, each opener gets a newly created library base that holds data
used by the AmiTCP/IP system. Each library base function makes sure
that the caller is from the right Amigatask and refuses to operate if
wrong task is attempting to use it (seesection 5.5 on page 100 for
detailed information).
4.10.2 The System Call Semaphore and Task Priorities
Currently, when program enters to some of socket library functions, it
attempts to get semaphore to hold othercallers executing library code
simultaneously. This is done so, since in Unix system,where this code
originally runs, doesn't pre-empt process that is executing system call.
In BSDSS, where ``Unix system calls'' run in user mode, system call
emulation glue uses a mutex to prevent simultaneous use of that part of
the server code. Although spl functions are used in NET/2 code to
prevent simultaneous access of criticalsections, there may still be some
sections that leave out protection if system call semaphore is removed.
Unnecessary system call semaphore usageis going to be removed in later
releases. Hopefully it, and the overhead it generates,becomes obsolete.
The priority of the application process is raised to the same with the
AmiTCP/IP, while the application executes AmiTCP/IP code. This is to
prevent situations where a process witha low priority gets blocked for a
long time while holding e.g. the system call semaphore, since otherwise
System Manual AmiTCP/IP Section 4.11 91
all networking programs would be blockedwith it. Thisapplies to all
semaphores used internally by the AmiTCP/IP, not just the system call
semaphore.
Initialization Routines
Making application interface visible andoperative contains a few steps:
call to api_init() creates the master socket library base and initializes
semaphores and lists API needs. Library base is not inExec library base
list yet. Routine api_show() checks first if bsdsocket.library is
already in Exec list and if not, calls Exec AddLibrary() to make it
visible.
AmiTCP/IPcan remove socket library from the Exec list at any time,
i.e. make it not visible, by calling api _hide(). No new socket bases
can be opened after this call. Socket bases opened before api _hide()
operate normally. If AmiTCP/IP calls api_show() again, new libraries can
be opened.
api_setfunctions() takes all socket library bases out of operation. It
sets all function vectors in every socket base to return an error. This
function is called if AmiTCP/IP panic()ed and all libraries are expected
to be closed.
When application interface is to be removed from system, api_deinit()
is called. It waits for all opened libraries to closeand then calls
expunge function to deallocate the master socket base from the memory.
4.11 Configuration Variables
The configuration variable definitions are stored into a structure named
cfg_variable. It is defined in kern/amiga_netdb.h as follows:
/* Variable types */
/* Note: Query calls value, Set calls notify functions */
enum var_type
-
VAR_FUNC = 1, /* value is function pointer */
VAR_LONG, /* value is pointer to LONG */
VAR_STRP, /* value is pointer to string */
VAR_FLAG, /* LONG value is set once */
VAR_INET, /* struct sockaddr_in */
VAR_ENUM /* value is pointer to long, whose value is set
according to a enumeration string in notify*/
";
typedef LONG
(*var_f)(struct CSource *args, UBYTE **errstrp, struct CSource *res);
typedef int (*notify_f)(void *pt, LONG new);
/* Configureable variable structure */
struct cfg_variable -
enum var_type type; /* type of value */
92 Section 4.12 AmiTCP/IP System Manual
WORD flags; /* see below */
const UBYTE *index; /* optional index keyword list */
void *value; /* pointer to value... */
notify_f notify; /* notification function */
";
#define boolean_enum (notify_f)"NO=FALSE=OFF=0,YES=TRUE=ON=1"
/* Variable flags */
#define VF_TABLE (1<<0) /* with an index... */
#define VF_READ (1<<1) /* readable */
#define VF_WRITE (1<<2) /* writeable */
#define VF_CONF (1<<3) /* writeable only during configuration */
#define VF_RW (VF_WRITE_VF_READ)
#define VF_RCONF (VF_CONF_VF_READ)
#define VF_FREE (1<<8) /* free when replaced? */
The configuration file (by default AmiTCP:db/AmiTCP.config) is read
with the function readconfig(). This function also parses the command
line arguments.
4.12 Network Database
The network database is initialized by the init _netdb() function. This
function allocates the NetDataBase structure and parses the file
AmiTCP:db/netdb. The NetDataBase structure is definedas follows:
struct NetDataBase -
struct SignalSemaphore ndb_Lock;
struct MinList ndb_Hosts;
struct MinList ndb_Networks;
struct MinList ndb_Services;
struct MinList ndb_Protocols;
struct MinList ndb_NameServers;
struct MinList ndb_Domains;
";
This structure contains a lock and lists for the different network
database entries. The lock semaphore is obtained in the shared mode for
reading, and in the exclusive mode for writing. See section 2.5.1 for
the information about the different entries.
Chapter 5
Implementation Notes
This chapter describes some points of the implementation of the
AmiTCP/IP. The code that is not changedfrom the BSD net/2 -release is
not reviewed. [Leffler et al 1989 ] describes the design and
implementation of the BSD Unix, including the networking system.
[Leffler et al 1991b ] is also very helpful.
Most of the knowledge gathered during this project is gained by reading
the source code itself. This chapter does not try to make that totally
unnecessary.
5.1 Time outs
The Unix timeout() function implements the time out needs of the Unix
kernel. When kernel code calls timeout(), the functiongiven as argument
will be called after the specified timeout has elapsed. In AmigaOS time
outs are provided by the timer device. AmiTCP/IP sendstime out
requests to the device and gets them back when the time specified has
elapsed.
The functions called by the time out service are:
if_slowtimo()
This is the time out function of the network interfaces and is
defined in net/if.c.
arptimer()
Handles the time outs of ARP protocol (net/sana2arp.c).
pfslowtimo()
This is the main slow time out function for all protocols. It calls
the pr_slowtimo() function of each configured protocol. The interval
of this timer is 500 ms (kern/uipc_domain.c).
pffasttimo()
Is the corresponding function for fast (200 ms) time outs.
93
94 Section 5.1 AmiTCP/IP System Manual
The request structure used by AmiTCP/IP has few fields in addition to
the normal struct timerequest1. The structure is defined in
kern/amiga_time.h as follows:
struct timeoutRequest -
struct timerequest timeout_request; /* timer.device sees only this */
struct timeval timeout_timeval; /* timeout interval */
TimerCallback_t timeout_function; /* timeout function to be called */
";
In this implementation the time out functions themselves do not call
any time out services, but the functionsare called by timer_poll(),
which is called by main() when there might be a message to handle (e.g.
when the timer signal is received). timer_poll() checks the reply port
to see if there really is a message to handle. Afterthe time out
function is serviced timer_poll() sends the request back to the timer
device and returns.
Time outservice is implemented by files kern/amiga_time.c and
kern/amiga_time.h. The functions defined are:
ULONG timer_init(void)
Initializes the time out subsystem by allocating the IO requests and
opening the timer device. Note that AmiTCP/IP uses functions that
are new to version 36 of the timer device, so the code refuses to
success with Kickstart 1.3 (or lower).
Returns the signal mask to wait for if successful.
void timer_deinit(void)
Frees all resources used by time out subsystem.
void timer_send(void)
Sends time out requests allocated by timer_init() tothe timer
device.
struct timeoutRequest *createTimeoutRequest(TimerCallback_t fun,
ULONG seconds, ULONG micros)
Creates a new time out request. This can be called only after
successful timer_init().
void deleteTimeoutRequest(struct timeoutRequest *tr)
Deletes requests created by createTimeoutRequest().
BOOL timer_poll(VOID)
Checks if there are any timer requests in the reply port and if there
is handles them by calling the handleTimeoutRequest(). Then it sends
the request back to the timer device.
void handleTimeoutRequest(struct timeoutRequest *tr)
Inline function which simply calls the function specified in the time
out request.
________________________________
1See standard Amiga include file devices/timer.h
System Manual AmiTCP/IP Section 5.2 95
void sendTimeoutRequest(struct timeoutRequest *tr)
Inline function which sends the request to the timer device.
See the files kern/amiga_main.c and kern/amiga_time.c for example of
the usage.
5.2 Memory Management
5.2.1 Mbufs
Mbufs are ported just as they are. Memory is allocatedin small chunks
for both mbufs and clusters. The size of the cluster and the number of
mbufs to allocate in one chunk are configurable variables, see section
2.4 for summary of the configurable variables in general.
Note thatsince data must be copied at the SANA-II interface there is
no need to use trailer protocols (whosemain gain is avoidance of that
copy) and so the mbuf clusters need notbegin at page boundaries2. This
fact lead to the implementation of the clusters where the size of the
cluster may be arbitrary (now user configurable) and the reference count
of the cluster is stored in a little header before the actual data.
One noteon allocating memory for the mbufs: Since the mbuf must be
perfectly aligned (i.e. 128 byte mbuf must be 128--aligned), we need to
allocate one extra mbuf to be able to align the mbufs in arbitrary memory
chunk returned by Exec AllocMem().
The 'canwait' argument of the mbuf functions is ignored by now, more
memory will be allocated if limit of maximum memory usage is not hit.
This is all right as long as the mbuf allocation functions are not called
from interrupts. The only functions in the AmiTCP/IP which may get
called from the interrupt code are the SANA-II callback functions
m_copy_from_mbuf() and m_copy_to_mbuf()defined in file
net/sana2copybuff.c.
The function descriptions for the mbufs are on section 4.5. See files
sys/mbuf.h and kern/uipc_mbuf.c for the actual implementation.
5.2.2 malloc() & free()
Do not call the malloc() and free() functions directly! Since AmiTCP/IP
is multi--threaded program these functions are not safe, since they use
static data. File sys/malloc.h defines two inline functions to be used
instead. They are defined as follows:
static inline void * bsd_malloc(unsignedlong size, int type, int flags)
This function calls malloc() to allocate memory of size size and type
type. The types are defined in sys/malloc.h and are used for
bookkeeping purposes. The flags may have either value M_WAITOK or
M_NOWAIT. The flags are not used by this implementation, however, but
are defined for portability.
________________________________
2Well, there is no virtual memory in Amiga either.
96 Section 5.3 AmiTCP/IP System Manual
static inline void bsd_free(void *addr,int type)
Frees memory allocated by bsd_malloc().
The malloc() and free() are made safe by malloc_semaphore, which
protects mallocs and frees from collisions. It is obtained before the
actual calls to malloc() or free() and released after them.
In addition to these functions sys/malloc.h defines macro versions for
the most usual usages.
Initialization for these functions is done by:
BOOL malloc_init(void)
Initializes the malloc_semaphore. This must be called early in the
initialization process, since bsd_malloc() nor bsd_free() cannot be
called before malloc_semaphore is initialized.
5.3 Concurrency Control
5.3.1 Processor Priority Levels
In Unix systems the critical sections are mainly protected by raising the
processor priority level (i.e. preventing interrupts upto a certain
level). This crude way might hurt a real time operating system as the
AmigaOS, so it can not be implemented assuch. Besides, the AmiTCP/IP
runs as a normal user level process which has no needed privilege to
alter the interrupt levels of the processor.
The implementation in AmiTCP/IP is twofold; a semaphore is used in the
debugging mode and task switch prevention is used in the production
version. Using semaphore makes debugging easy as single stepping and
tracing is possible while keeping the system alive. The semaphore adds
certain overhead which is not acceptablein the production version, so
the prevention of the task switches is used.
When thepreprocessor symbol DEBUG is defined the spl_semaphore is
used. When this semaphore is free the process is at level SPL0 (user
level) and when the semaphore is allocated the process is at
``interrupt'' level (SPLSOFTCLOCK, SPLNET or SPLIMP), effectively
disallowing anyone else to enter critical section. When the symbol DEBUG
is not defined the functions and macrosare defined differently. They
manipulate directly the Task Disable Nest Count (TDNextCnt field of the
SysBase). This field is normally used by Exec functions Forbid() and
Permit() which increment and decrement the value of the field,
respectively. Since an assembler macro is provided byCommodore for the
Forbid() function, the semantics of thefield cannot change in the future3.
The semantics of the TDNestCnt is littleabused by the AmiTCP/IP, however.
The value of the spl level in question is directly assigned as the value of
the field. This is not visible outside of the AmiTCP/IP, since basically
functions which use this field either directly or indirectly (via Forbid()/Perm*
*it())
need to return the value of the field asit was when the function was called.
This is also the semantics of the usageof the spl functions.
________________________________
3If it would, then all the codeusing that official macro would break.
System Manual AmiTCP/IP Section 5.3 97
The spl_semaphore is initialized by the function BOOL spl_init(void)
which is called among the very first functions in the initialization
process.
The function spl _n(int) (defined in sys/synch.h) is used to alter the
priority levels. In the non-debugging mode there are two other
functions, too: spl_const(), which isused with a constant (non-zero)
argument, and spl_0() which is used to switch to the level 0. In
addition, a macro has been defined for each separate level for
portability. The macros are as follows:
spl0() switches to the normal execution level.
splsoftclock() is the level on which timer eventsare executed.
splnet() is the level of the network interrupts in UNIX.
splimp() is the highest of the priority levels used in the networking
code. For example, mbuf functions are executed at this level.
These macros return the previous level which may then be set back with
splx(int)-macro, which sets the level tothe level given as argument.
5.3.2 Sleeping Facilities
Sleeping facility is implemented by kern/kern _synch.c. Processes sleep
on a channel, which is the key used to identify sleepers. Usually this
is some address which is unique to the calling process. Socket base
structure for the sleeping task is linked in the sleep queue before the
actual sleep is started. This is how the waking task can find the
sleeper to wake up when something happens on the channel the process is
sleeping on.
The sleepqueue is implemented as a hash table, where the channel value
is mapped to an index of a sleep queue with the hash function4
SLEEP_HASH() (defined in kern/kern_synch.c).
The actual sleep is implemented by sending a time out request for the
time out duration to the timer device. The sleep completes on the time
out, a wakeup, a break signal or users specified signal.
Note thatall critical resources (e.g. semaphores) must be freed
before sleeping, since otherwise the whole networking code hangs.
tsleep() does this for you.
The functions which implement the sleep system are:
BOOL sleep_init(void)
This initializes the sleep_semaphore and the sleep queues. Thismust
be called before any other functions of this module.
int tsleep(struct SocketBase *p, caddr_tchan,
char *wmesg, const struct timeval *time_out)
________________________________
4Actually a macro.
98 Section 5.3 AmiTCP/IP System Manual
This function is the function usually called by the processes5 and
implements the sleep by using the other functions of this module.
The caller goes to sleep for at most the time specified in the struct
timeval argument. chan is the channel to sleep on. wmesg is a
string which is marked in the socket base (p) as the reason to sleep.
Currently no-one ever reads it, though.
void wakeup(caddr_t chan)
Wakes up any sleepers on channel chan. Searches the sleep queue for
entries with key chan and wakes them up by first clearing the key
(p_wchan field of the socket base structure) and then signalling the
process with the signal of the time out message. The usage of the
sleep queues and the p_ fields in the socket base structures are
protected with sleep_semaphore, which must be obtained before even
reading the sleep queues.
Note that since a task in AmigaOS may get signals anytime, the
sleeper checks the p_wchan field on reception of thesignal and if it
is nonzero it goes to sleep again.
void tsleep_send_timeout(struct SocketBase *p,
const struct timeval *time_out)
First ensures that the message previously sent to the timer device is
back. Then sends timer device a time out request for duration
specified in time_out if it is not NULL. The requestsent is
allocated when the library is opened.
void tsleep_abort_timeout(struct SocketBase *p,
const struct timeval *time_out)
Aborts the time out sent by the tsleep_send_timeout(). This function
must be used when the time out must be cancelled (when the sleeper is
waken up).
This function just sets the timer reply port (timerPort field of the
socket base) to the mode in which reception of the message does not
cause any action.
void tsleep_enter(struct SocketBase *p,caddr_t chan, char *wmesg)
Puts the caller on to a sleep queue.
int tsleep_main(struct SocketBase *p, ULONG wakemask)
Waits for either time out, wakeup, break or user defined action to
happen. The sigIntrMask field of the socket base structure defines
which signals will cause a break. Return value of EINTR is returned
if any of the signals specified in that mask are received. In
addition the signals are set back with SetSignal() for the user
program to be able to detect them. The wakemask argument specifies a
mask for signals which should cause a return from the sleep. In such
case the return value ERESTART is returned.
________________________________
5Only WaitSelect() system calluses sleeping facilities without this
function.
System Manual AmiTCP/IP Section 5.4 99
On exit the process is guaranteed not to be in the sleep queues any
more, but the time out remains active if it is not the reason for
return. Return value on wakeup is 0 and on time out EWOULDBLOCK is
returned.
5.4 Socket Library Creation Procedure
Since a new socket base is created eachtime a different task opens the
AmiTCP/IP socket library, the procedureis a bit more complicated than
on libraries where the same library baseis returned (See
[RKM Libraries 1992 ]). There is, for example, two socket library bases
in use. All code discussed here is located in api/amiga _api.c.
5.4.1 Master Library Base
This is the library base that is made shown by api_show() (see section
4.10.2). It is placed in Exec's librarylist. This is of type struct
Library and contains information that anoutsider can read by scanning
through the Exec library list. Information available is version and
revision numbers and count of tasks thathave (application) library base
open.
Master library base has only functions ELL_Open() and ELL_Expunge().
When applications opens the socket library, the Exec calls ELL_Open().
This function creates new application socket bases and increments the
reference count of open application library bases. Ifthe calling task
has a socket base open already, a new socket base is not created but the
reference count of task's socket base isincremented and the base pointer
is returned to the caller. This feature has many useful possibilities,
for example in intermediate libraries which need to manipulate the
sockets of the calling task.
ELL_Expunge() does (not) do one task. When it is called, it checks if
there is any libraries still open or ifAmiTCP/IP lets this function
execute further (in fact, currently thissecond check is sufficient since
only AmiTCP/IP can close the library andit doesn't do it until all
bases has been closed. The next Remove() is there forfuture reference
too). Then, the memory of the master library base is deallocated and
NULL is returned (no AmigaDOS seglist tofree). The SIGBREAKF _CTRL_C
signal that is sent with the global varianble SB _Expunged set to TRUE
notifies api_deinit() function about the fact that now all libraries are
closed.
5.4.2 Application Library Bases
These are the library bases that are returned to the openers of the
socket library. In this base the Open() function is obsolete since all
OpenLibrary() calls go through the master socket base. Exec and
AmiTCP/IP generated Expunge() calls go also through the master socket
base.
UL_Close() is the close function for all application library bases.
First it decrements the reference countof this base and returns NULL if
100 Section 5.5 AmiTCP/IP System Manual
there are still references left (again,NULL informs Exec that there is
no AmigaDOS seglist needed to be removed).
If thereare no more references to this library base, following steps
are taken to remove it from the memory: All socket descriptors still
open are closed. The base is removed from the AmiTCP/IP list of open
application socket bases. The timer request is deallocated. Then the
library base is removed from the memoryand open count of application
socket bases is decremented in the master library base. Finally,
ELL_Expunge() is called if the open count reached zero and the LIBF_DELEXP
flag is set (by a previous ELL_Expunge() call).
5.5 The SocketBase Structure
The SocketBase structure is defined in file api/amiga_api.h as follows:
struct SocketBase -
struct Library libNode;
/* "Global" Errno */
WORD errnoSize;
/* -- now we are longword aligned -- */
UBYTE * errnoPtr; /* this points to errno */
LONG defErrno;
/* Task pointer of owner task */
struct Task * thisTask;
/* task priority changes (WORDS so we keep structure longword aligned) */
WORD myPri; /* task's priority just after libcall */
WORD libCallPri; /* task's priority during library call */
/* -- descriptor sets -- */
WORD dTableSize;
WORD nextDToSearch;
struct socket ** dTable;
/* AmiTCP signal masks */
ULONG sigIntrMask;
ULONG sigIOMask;
ULONG sigUrgMask;
/* -- these are used by tsleep()/wakeup() -- */
char * p_wmesg;
queue_chain_t p_sleep_link;
caddr_t p_wchan; /* event processis awaiting */
struct timerequest * tsleep_timer;
struct MsgPort * timerPort;
/* -- pointer to select buffer during Select() -- */
struct newselbuf * p_sb;
/* -- per process fields used by various'library' functions -- */
/* buffer for inet_ntoa */
char inet_ntoa[20]; /* xxx.xxx.xxx.xxx"0 */
/* pointers for data buffers that MAY beused */
struct DataBuffer selitems;
struct DataBuffer hostents;
struct DataBuffer netents;
struct DataBuffer protoents;
struct DataBuffer servents;
";
System Manual AmiTCP/IP Section 5.6 101
libNode is a normal library base structure and is used by the system.
Since in this implementation each openergets a task specific library
base, AmiTCP/IP links all ``user librarybases'' together using Node
field of libNode.
When socket library function encounters an error, it saves the value of
the error to the memory location addressed by errnoPtr. errnoSize
specifies the size of the variable pointed by the errnoPtr. By default
errnoPtr points to the defErrno but canbe changed to point any memory
location -- usually to the global errnovariable in the context of the
user task.
In entryof each bsdsocket.library function call, value of thisTask is
compared to the task pointer of callingtask to make sure right task is
calling the function. This variable is also used to find library base of
some executing task.
When taskis executing system calls in bsdsocket.library, its process
priority is changed to the same as thatof the AmiTCP/IP task in order
not to hold semaphores and block the whole network system. If some
higher priority process becomes active and a lower priority task is
holding some vital semaphore of the AmiTCP/IP then the precess cannot
continue to run. The manipulation of the process priorities uses myPri
and libCallPri fields of the socket base.
dTableSize is the number of current maximum limit of socket
descriptors. dTable is the descriptor table containingpointers to
socket structures, i.e. sockets. nextDToSearch makes searching of free
socket descriptor faster6.
sigIntrMask is a task specific mask of the signals which should break
the Wait() call in the tsleep_main(). Reception of such signals causes
the system calls to return -1 and the error code pointed by errnoPtr to
be set to EINTR. sigIOMask field specifies the signals to send when
asynchronous notification is requested. Signals specified in sigUrgMask
are sent when out of band data is received. These masks implement the
functionality of the SIGIO and SIGURG signals of the Unix systems,
respectively. All these masks can be set with the SetSocketSignals() API
call. The default mask for the sigIntrMask specifies the signal for the
ctrl-C, other two are zero by default.
The nextgroup of variables are used by tsleep() and wakeup(). p_wmesg
points to a string telling the reason why task is sleeping. p _sleep_link
is used to chain library bases in the sleep queues. Waiting channel key
is hold in variable p_wchan and data handling time outs in variables
tsleep_timer and timerPort (more about this in section 5.3.2 on page 97).
WaitSelect() inserts one selitem on each socket it wants event
information of. p_sb points to a newselbuf that contains these items.
Some APIfunctions in the original environment use static buffers to
store their output. As a shared library cannot use static buffers (to be
re-entrant), the buffers must be allocated dynamically. The SocketBase
structure has space for the output buffer for the Inet_Ntoa() and
pointers for other needed buffers. These buffers are allocated only when
needed.
________________________________
6Semantics of allocating lowestfree socket descriptor is preserved.
102 Section 5.6 AmiTCP/IP System Manual
5.6 The Application Program Interface
Most of API code is original NET/2 codetaken from BSDSS7. BSDSS mutexes
are replaced by Amiga semaphores and struct proc references are changed
to references to our socket library base8.
Many functions used copyin() and copyout() to copy data around. Those
functions copy data between system and user space (different virtual
memory mappings) in original BSD Unix system. It is also possible that
those functions will fail e.g. if user tries to reference illegal memory
locations. In AmiTCP/IP system copyin() and copyout()functions are
replaced with bcopy(), arguments are thesame but the bcopy() never
fails. Therefore some obsolete checks are removed fromthe code.
5.6.1 HowAPI Functions Are Ported
Most functions in our API are ported from BSD Unix system calls. BSD
Unix system call interface calls the actual function with three
arguments. First is user process structure pointer. Second contains the
given arguments and is locally named asuap structure. Third argument is
a pointer to the return value. The function returns error value or 0 if
no error occurred.
The socket() function is a good example of this:
socket(p, uap, retval)
struct proc *p;
registerstruct args -
int domain;
int type;
int protocol;
" *uap;
int *retval;
The system function interface maps directly to Amiga shared library.
Since every task has socket library baseof its own, Unix process pointer
matches to library base pointer given inregister A69 . uap argumentsare
passed in registers normally. Return value is returnedin the register
D0 (as any standard C compiler does). So the *retval was changed to a
local variable retval and removed, whennot needed. The returned value
is -1 on error, in which case the errnois also set to indicate the error
(see file sys/errno.h for list of errorcodes), or retval, or 0 if no
other return value is needed.
To emulate Unix system call interface, each function first obtains the
syscall_semaphore (why,see section 4.10.2 on page 90) and while this
task is holding the semaphore, no othertask can continue to execute the
________________________________
7BSDSS networking code is almost completely the same.
8See 5.5.
9All Amiga shared libraries expect them to be called relative to
register A6
System Manual AmiTCP/IP Section 5.7 103
library code10. This means that every system call function needs to
release syscall_semaphore before returning. To accomplish this, each
return inside the function is changed togoto Return; and at label
Return: is code that releases the syscall_semaphore11 .
The default modifications were: changing file descriptor tables and
pointers to socket tables and pointers,respectively, removing usage of
struct fileops function pointers -- replacing them with direct socket
functions, changing copyin() and copyout() functions to bcopy()s -- no
more error checking here needed, and last, changing parameters on
tsleep() calls.
5.6.2 APIFunctions Which Needed More Modifications
IoctlSocket() (Former ioctl()): Non-socket stuff removed, and ioctl code
from soo_ioctl() inserted.
WaitSelect() (Former select()) usedto count remaining time out time if
tsleep() returned accidentally too early. AmiTCP/IP uses the timer
device for its time outs and the time out request is aborted only
when it is needed again, so there is no need to send new time out
requests (and to calculate their time out durations). tsleep() is
broken apart into pieces so that the time out request is sent only
once.
CloseSocket() Decreases socket's referencecount and calls soclose() to
kill the socket if it becomes zero.
socket() and accept() Added initialization of so_refcnt. fdAlloc()
doesn't bind fd to socket. It is done explicitly.
Resolver functions used to allocate huge amounts of stack. Now memory is
allocated dynamically from the head using bsd_malloc.
5.7 Changes in Functions Below API Level
Functions that API functions call are mostly functions that use struct
socket type arguments, possibly having some other arguments too. In most
cases no modifications were needed. There was some modifications, like
parameters for tsleep() call, which hadto be changed throughout the
code.
5.7.1 Other Changes
selscan() calls soo _select() directly, and uses socket pointer instead of
file pointer.
soo_select() also uses socket pointer instead of file pointer.
________________________________
10Not all functions require obtaining syscall_semaphore so those can continue
to run.
11syscall_semaphore is also freed when library function does tsleep().
104 Section 5.8 AmiTCP/IP System Manual
socreate() allows allsockets to be privileged. This means that user can
obtain raw sockets and use normally privileged port numbers.
sosend() uses uioread() instead of the original uiomove(). sblock() and
sbwait() are called with library base pointer as the second argument.
soreceive() uses uiowrite() instead of the original uiomove(). See above
about the modification of call sblock() and sbwait().
sorflush() calls sblock() with base pointer argument as NULL.
sosetopt() and sogetopt() : type of so_linger and so_timeo fields in
socket structure is changed from short int to struct timeval.
Manipulation of these data is changed accordingly.
sbwait() takes socket base pointer as second argument. It is then passed
to tsleep() (see section 5.3.2 on page 97).
sblock() and sb_lock(): sblock() is a macro that calls sb_lock(). Both
take socket base pointer as second argument. sblock() forwards that
pointer directly to sb_lock() which, again, passes it to the
tsleep().
5.8 Agnet.device
We used the agnet.device, an SANA-II test device, to test and develop the
AmiTCP/IP network code. The usage and features of theagnet.device are
described in the section 1.7.1, page 10.
SANA-II is an standard network device driver interface for the Amiga
(see [SANA-II 1992 add ]). It is an extension to the normal device
interface, which is described in the [RKM Libs & Devs 1989 ]. Device
drivers are accessed by their name fromthe system list. Drivers may be
loaded dynamically from the disk, if they are not currently in the main
memory. There may be several units sharing common driver code so each
(network) device is specified by an unitnumber and the device driver
name.
We wrotethe agnet.device using the SLIP driver which Commodore has
provided as the example code for SANA-IIdrivers. However, there was
some problems with the supplied code. First, the SLIPdriver code
obviously follows an obsolete SANA-II draft. There was some
modifications in the final standard e.g. in the eventhandling and
multicast addressing.
The provided example code was also very fragile, it did not get
compiled as such with the newer SAS C version 6. Codedepended on some
features of the SAS C 5.10. For example, it always expected to find
device base pointer in address registerA6.
5.8.1 IO Commands
There is a detailed description of SANA-II device commands and functions
in [SANA-II 1992 add ]. The following IO commands are implemented in
agnet.device:
System Manual AmiTCP/IP Section 5.8 105
CMD_CLEAR
This standard command should return IOERR_NOCMD when issued to
SANA-II device.
CMD_INVALID
This standard command should return IOERR_NOCMD.
CMD_READ
Get the next packet available of the requested packet type. The data
returned (via a call to the requester-provided CopyToBuffer()
function) is the Data Link Layer packet data only. Raw packets are
not supported.
CMD_RESET
This standard command should return IOERR_NOCMD when issued to
SANA-II device.
CMD_START
This standard command should return IOERR_NOCMD when issued to
SANA-II device.
CMD_STOP
This standard command should return IOERR_NOCMD when issued to
SANA-II device.
CMD_UPDATE
This standard command should return IOERR_NOCMD when issued to
SANA-II device.
CMD_WRITE
Send packet to the network. Raw packets are not supported. Sending
packet with a broadcast hardware address is not supported.
S2_BROADCAST
Broadcast a packet to the network. Raw packets are not supported.
S2_CONFIGINTERFACE
Configure the interface. The address field will be set depending on
the specified hardware type.
S2_DEVICEQUERY
Report the statistical information about the device.
S2_GETGLOBALSTATS
Report accumulated statistics as defined in struct Sana2Devicestats.
S2_GETSTATIONADDRESS
Report the ``hardware'' address for the unit. Before the
configuration, the current hardware address has all bits set. The
default hardware address is not stored anywhere.
S2_GETTYPESTATS
Report accumulated statistics of the tracked packets.
S2_OFFLINE
Remove interface from service. Flush all queued IO requests.
106 Section 5.8 AmiTCP/IP System Manual
S2_ONEVENT
Return when specified event(s) occur(s).
S2_ONLINE
Put the interface back in service. This command resets the unit
statistics.
S2_READORPHAN
If there is no pending CMD_READ request with the type of the received
packet, the packet is given to first pending S2_READORPHAN request.
The data returned (via a call to the requester-provided CopyToBuffer
function) is the Data Link Layer packet data only. Raw packets are
not supported.
S2_TRACKTYPE
Start tracking of the specified packet type packets.
S2_UNTRACKTYPE
Stop tracking of the specified packet type packets.
Uninplemeted IO Commands
These SANA-II device commands are not supported.
CMD_FLUSH
This standard command returns IOERR_NOCMD when issued to the
agnet.device.
S2_ADDMULTICASTADDRESS
This SANA-II command is not supported. It returns IOERR_NOCMD.
S2_DELMULTICASTADDRESS
This SANA-II command is not supported. It returns IOERR_NOCMD.
S2_GETSPECIALSTATS
This SANA-II command is not fully supported. It returns an empty
Sana2SpecialStat structure.
This command should report accumulated driver specific statistics.
This includes ethernet ``retries''.
S2_MULTICAST
This SANA-II command is not supported. It returns IOERR_NOCMD.
5.8.2 Initialization Procedure
agnet.device must be started as a DOS process by the Run command. The
dynamic loading is not yet implemented. Its own startup module, init.c,
opens needed libraries, initializes device base and calls the main()
function. Main function opens timer.device and initializes the ARexx
port; if initialization was successful,it adds the device base to the
system list.
In the main loop the device task waits for three different events:
user or Expunge() generated break signal(SIGF _BREAK_F), ARexx messages or
user IO request messages.
System Manual AmiTCP/IP Section 5.8 107
5.8.3 TheDevice Interface Functions
The device interface contains 6 standardlibrary calls in the device
base. The device may be opened or closed, the IO requests may be
initiated or aborted and the system mayreclaim storage allocated by the
device driver.
These library calls are not normally executed directly by the user code
but instead higher level convenience functions in the Exec. The device
base library calls are made in the context of the caller, so some
synchronous IO commands may be executedlike library calls without
message passing overhead (quick IO).
The synopsis of the functions specifies the registers where the call
parameters are passed (REG(rn)).
Opening an Unit
An IO device is opened by the Exec function call OpenDevice(). When Exec
has found the named device driver in thesystem list it calls the special
DevOpen() function from the device base. DevOpen() function has
following synopsis:
ULONG ASM DevOpen(REG(a1) struct IOSana2Req *ios2,
REG(d0) ULONG unit, REG(d1) ULONG flags)
The device open function tries to allocate and initialize various
resources for the specified unit if the unit does not already exist.
The initialization routine InitUnit() is called; if it returns an
unit structure, a private buffermanagement structure is filled from
the user provided tag list. The user supplied IO request is filled
with appropriate values.
The promiscuous or exclusive modes are not supported.
struct AgnetDevUnit *InitUnit(ULONG);
The initialization routine allocates an unit structure and then calls
the configuration routine ReadConfig(). If the configuration file
was read and interpreted without errors the lists and locks in the
unit structure is initialized. The unit is then put online and unit
structure is added to the device base.
BOOL ReadConfig(struct AgnetDevUnit *adu)
The configuration routine attempts to read in the configuration file
for the given unit. It strips the comments out of the file and
provides the file as a single line to the parsing routine
ParseConfig(). If there is no configuration file, the parsing
routine is called with an empty line.
Closing an Unit
The accessed unit is closed after use with the Exec CloseDevice()
function. It runs the DevClose() function from the device base. The
call has the following synopsis:
108 Section 5.8 AmiTCP/IP System Manual
BPTR ASM DevClose(REG(a1) struct IOSana2Req *ios2)
The device close function calls the unit close function. If the
device has been asked to Expunge() itself, the close function sends
an appropriate signal to the device task. The device task then
performs the postponed expunge function.
The DevOpen() and DevClose() calls are executed while Forbid()'den
unless the code explicitly Wait()'s.
Initiating an IO Request
The DevBeginIO function from the devicebase is called to initiate an IO
command. This call is made in the context of the requesting task (task
calling Exec functions DoIO() or SendIO()). The DevBeginIO() call has
the following synopsis:
VOID ASM DevBeginIO(REG(a1) struct IOSana2Req *ios2)
The IO request is sent to the device task for dispatching and
execution. Currently all IO requests are executed in the context of
the device task, i.e. no quick IO is supported.
Aborting an IO Request
Some IO requests may be aborted before they are completed by Exec
function call AbortIO(). The aborting function has thefollowing
synopsis:
VOID ASM DevAbortIO(REG(a1) struct IOSana2Req *ios2)
Currently only the CMD_READ, S2_READORPHAN, CMD_WRITE, S2_BROADCAST,
and S2_ONEVENT IO commands may be aborted. Other IO commands are
executed in an atomic way and can not be aborted reliably.
Expunging the Device
The system may reclaim the storage allocated by the device driver by
calling the DevExpunge() function. Memory reclaiming is normally done in
a low memory situation or after a user requested memory flush. The
expunging function has the following synopsis:
VOID ASM DevExpunge(VOID)
The DevExpunge() may not Wait() because it may be called from the
Exec function AllocMem() protected by Forbid()/Permit() pair.
The DevExpunge() function call currently signals the device task,
which performs the actual expunging by calling the DoExpunge(). Each
unit structure is expunged by the function ExpungeUnit().
System Manual AmiTCP/IP Section 5.8 109
5.8.4 Packet Delivery
The packet sent to the pseudo network may be delayed or mutated randomly.
A special structure (struct DelayRequest) stores the packet type and data
during the ``transmit delay''. The delay is implemented by sending this
structure as a the timer IO request to the timer device. The packet
transmit functions are as follows:
VOID WritePacket(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
The function checks that the unit is on line and checks for the legal
data length. Then the routine adds the given IO request (CMD_WRITE,
S2_BROADCAST) into send queue. If there is a free delay request the
SendPacket() is called immediately.
VOID SendPacket(struct DelayRequest *delayed)
The SendPacket() function takes an empty DelayRequest structure as
its argument. First, it gets a send IO request from the send queue.
The request is immediately returned if the packet is ``lost'' during
transmit. This is repeated until a packet is found which is not to
be lost.
The packet data, type, length, transmission type and the address of
the sender are then copied into the DelayRequest structure. If
needed, bit errors are made into packet data.
If there is specified delay for the unit, the request is sent to the
timer device. After the delay the timer device returns request into
the device port and it is dispatched by the ReceivePacket() function.
If there is no delay, the ReceivePacket() is called immediately.
VOID ReadPacket(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
The function checks that unit is online, then adds the read request
into an appropriate queue.
VOID ReceivePacket(struct DelayRequest *delayed)
The ReceivePacket() function calls the DoReceive() function on each
unit the packet is destined. It then handles the DelayRequest
structure back to the SendPacket() function.
VOID DoReceive(struct AgnetDevUnit *adu,struct DelayRequest *delayed)
The DoReceive() function tries to find a receive IO message waiting
for this particular type packets. If none is found, the first
S2_READORPHAN IO request is selected.
The CopyBack() function sets the appropriate IO request return values
and copies the packet data to the receive buffer.
5.8.5 Arexx Interface
The ARexx interface is implemented by using the SimpleRexx package
provided in Commodore Native Developer Update 2.0 kit. The Arexx
commands executed by the agnet.device are described in section 1.7.1,
page 12.
110 Section .0 AmiTCP/IP System Manual
LONG ParseRexx(UBYTE *arg, UBYTE **errstr, UBYTE **result)
The ARexx command string is copied into a buffer and passed to the
parser function ParseRexx().
The parser allocates a DOS struct RDArgs structure for the ARexx
command. This structure holds the information for the DOS parsing
functions. The first keyword in the string is read and tokenized by
the DOS functions ReadItem() and FindArg().
The rest of the line is parsed according this token. In the case of
Query and Unit commands the unit number is read from the command line
and the rest of the line is passed to the appropriate functions.
LONG ParseConfig(struct AgnetDevUnit *adu, struct RDArgs *rdargs,
STRPTR *errormessage)
The command line stored into the RDArgs structure is parsed by the
DOS function ReadArgs(). The parsed configuration information are
then gathered, its legality is checked and it is stored into the unit
structure.
LONG ParseQuery(struct AgnetDevUnit *adu, struct RDArgs *rdargs,
STRPTR *errstr, STRPTR *result)
Like the ParseConfig(), ParseQuery() parses the command line by the
ReadArgs(). It then fills the reply buffer by the requested
configuration parameter values and then makes an ARexx string out of
the buffer. This string is then returned to the ARexx process.